/*
    Spherical Grid Volume Approximation: An algorithm for approximating the 
    volume of a compound object by keeping track of its members' positions in space.
    Copyright (C) 2009, Rodrigo Castano, FuDePAN

    This file is part of Spherical Grid Volume Approximation.

    Spherical Grid Volume Approximation is free software: you can redistribute 
    it and/or modify it under the terms of the GNU General Public License as published 
    by the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    Spherical Grid Volume Approximation is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#include <iostream>
#include <sys/types.h> 
#include <dirent.h> 
#include <vector>
#include "grillado_original.h"
#include <fstream>
#include <string>

#define LIST_NAME 1
#define EXT 2
#define OUT_NAME 3
#define HELP 4
#define ALLOW 5

using namespace std;
static bool is_target (const string &ext_input, const dirent &, const string &);
static void leer(istream& entrada, ostream& salida, GrilladoOriginal &g, vector<Volume> &parciales);
static bool is_file(const dirent &ent);
static int type_arg(char *);
static void show_usage(char * argv [], string &ignored);
static bool parse_args(int argc, char * argv [], string &ext_input, string &list_name, string &output_name, string &ignored) ;
static string extension(const string & name);
static bool contains(const string &c, const string &name) ;

int main(int argc, char * argv[] ) 
{ 
   
    // If a file name contains any of the characters in ignored
    // such file will not be processed.
    string ignored = "~";
    
    // Files with an extension matching ext_input will be processed.
    string ext_input;
    
    // Default value for the name of the output file with the volumes
    // calculated with Grillado.
    // Can be configured with command line argument -o "some_file_name"
    string output_name = "output.out";
    
    // Default value for the name of the output file with the list
    // of processed files.
    // Can be configured with command line 
    // argument -l "some_list_file_name"
    string list_name = "list.out";
    if(!parse_args(argc, argv, ext_input, list_name, output_name, ignored)) {
      return 0;
    }
    
    DIR *dir = opendir("."); 
    ofstream salida(output_name.c_str());
    GrilladoOriginal g(100,100,100,4.0f,5.7f);
    ofstream list(list_name.c_str());
    vector<Volume> parciales;
    if(dir) 
    { 
        struct dirent *ent; 
        while((ent = readdir(dir)) != NULL) 
        { 
	    	    
	    if(is_target(ext_input,*ent,ignored)) {
	      ifstream entrada(ent->d_name);
	      list << ent->d_name << endl;
	      leer(entrada, salida, g, parciales);
	    }
        } 
    } 
    else 
    { 
        cout << "Error opening directory." << endl; 
    } 
    return 0; 
}

static bool parse_args(int argc, char * argv [], string &ext_input, string &list_name, string &output_name, string &ignored) {
  string ignored_copy = ignored;
  if(argc<3) {
    show_usage(argv, ignored_copy);
    return false;
  }
  bool res = false;
  for(int i= 0; i<argc ;++i) {
      switch (type_arg(argv[i])) {
	case HELP:
	  
	    show_usage(argv, ignored_copy);
	    return 0;
	  
	  
	case EXT:
	  
	    ext_input = string(argv[i+1]);
	    res = true;
	    break;
	  
	case LIST_NAME:
	  
	    list_name = string(argv[i+1]);
	    break;
	  
	case OUT_NAME:
	  
	    output_name = string(argv[i+1]);
	    break;
	    
	case ALLOW:
	    ignored = "";
	    break;
	  
      }
    }
    if(!res) {
      
      cout << "You need to specify the extension of the files to be processed." << endl;
      cout << "For information on usage type: " << endl;
      cout << argv[0] << " -help" << endl;
    }
    if(output_name=="") {
      cout << endl <<  "You need to specify a non-empty name for the output file." << endl; 
      res = false;
    }
    if(list_name=="") {
      cout << endl << "You need to specify a non-empty file name for the list of processed files." << endl;
    }
    if(extension(list_name)==ext_input || extension(output_name) == ext_input) {
      cout << endl << "Error:" << endl;
      cout << "Either the output file name extension or the list of processed files file name extension matches the extension of the files to be processed." << endl;
      cout << "Please choose a different extension for these files." << endl;
      res =false;
    }
    if(contains("."+'"',ext_input)) {
      cout << endl << "Error:" << endl;
      cout << "The extension you provided contains the '.' or '"<< '"' << "' characters." << endl;
      cout << "Please choose a valid extension." << endl;
      res = false;
    }
    
    return res;
}

static string extension(const string &name) {
  string res;
  if(name.rfind(".")!=string::npos) {
    res = name.substr(name.rfind(".")+1);
  }
  return res;
}

static bool is_target (const string &ext_input, const dirent &ent, const string &ignored) {
  return extension(string(ent.d_name))==ext_input && is_file(ent) && !contains(ignored,string(ent.d_name));
}

static bool contains(const string &c, const string &name) {
  bool res = false;
  for(int j=0;j<c.size();++j) {
    res = res || (name.find(c[j])<name.size());
  }
  return res;
}
static void leer(istream& entrada, ostream& salida, GrilladoOriginal &g, vector<Volume> &parciales) {
	parciales.clear();
	g.reset();
	string cualquiera;
	float x, y, z;
	int contador = 0;
	while(entrada.good() && entrada >> x && entrada >> y && entrada >> z) {
		g.add_element(x,y,z); 
		getline(entrada,cualquiera);
		parciales.push_back(g.obtener_vol_parcial());
	}
	float densidad = parciales[parciales.size()-1]/float(parciales.size());
	salida.precision(30);
	salida << parciales.size() << " " << densidad << endl;
}

static bool is_file(const dirent & ent) {
  bool res = ent.d_type == DT_REG;
  return res;
  
}



static void show_usage(char * argv[], string &ignored) {
  cout << "Usage: " << endl;
  cout << argv[0] << " -e " << '"' << "ext" << '"' <<" [Options]" << endl << endl;
  cout << "-e " << '"' << "ext" << '"' << endl;
  cout << "indicates all files with extension ext will be processed with Grillado." << endl;
  cout << "ext cannot contain the '.', ' ' or '"<< '"' << "' characters." << endl << endl;
  cout << "Options:" << endl;
  
  cout << "-l "<< '"'<< "some_other_file.out" << '"' << endl;
  cout << "The list of processed files will be written to some_other_file.out." << endl;
  cout << "The default output file for the list of processed files is list.out" << endl << endl;
  
  cout << "-o "<< '"'<< "some_file.out" << '"' << endl;
  cout << "The output will be written to some_file.out, in the same order as the files in the list." << endl;
  cout << "The default output file is output.out." << endl<< endl;
  
  cout << "-a " << endl;
  cout << "File names that include characters in the folloring list will not be ignored." << endl;
  cout << "Normally ignored characters: " << ignored << endl << endl;
  
  cout << "Example1: " << endl << argv[0] <<" -e "<< '"' << "in" << '"' << endl;
  cout << "will process all files named " << '"' << "filenane.in" << '"' << endl << endl;
  cout << "Example2: " << endl << argv[0] << " -e " << '"' << '"' << endl;
  cout << "will process all files without extension." << endl;
  
}
static int type_arg(char * c) {
  string in = string(c);
  if(in=="-help" || in=="--help" || in=="-?") {
    return HELP;
  }
  
  if(in=="-a") {
    return ALLOW;
  }
  
  if(in=="-e" ) {
    return EXT;
  }
  
  if(in=="-l" ) {
    return LIST_NAME;
  }
  
  if(in=="-o" ) {
    return OUT_NAME;
  }
  return 0;
}
